package com.wiseasy.smartpos.cashierdemo;

import static android.os.Build.MODEL;
import static com.wisepos.smartpos.cardreader.CardSlot.CARD_SLOT_IC;
import static com.wisepos.smartpos.cardreader.CardSlot.CARD_SLOT_MAG;
import static com.wisepos.smartpos.cardreader.CardSlot.CARD_SLOT_RF;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_ICC_INVALID_ATR;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_ICC_NO_ATR;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_IC_MAG_ANOTHER_CARD;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_MSR_SWIPE_WHEN_RF;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_NO_SUPPORTED_APP;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_PIN_BYPASS;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_P_PSE_APDU_NOT_9000;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_SEE_PHONE;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_SEL_APP_APDU_NOT_9000;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_SUCCESS;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_SVR_INVALID_PARAMETER;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_SVR_PINPAD_SCERRN_OFF;
import static com.wisepos.smartpos.errorcode.WisePosErrorCode.ERR_USE_ANOTHER_INTERFACE;
import static com.wisepos.smartpos.keymanager.KeyType.KEY_TYPE_PEK;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.wiseasy.smartpos.cashierdemo.adapter.LogListViewAdapter;
import com.wiseasy.smartpos.cashierdemo.bean.LogBean;
import com.wiseasy.smartpos.cashierdemo.pinpad.IPinpadDialog;
import com.wiseasy.smartpos.cashierdemo.pinpad.PinpadDialog;
import com.wiseasy.smartpos.cashierdemo.util.CLog;
import com.wiseasy.smartpos.cashierdemo.util.Convection;
import com.wiseasy.smartpos.cashierdemo.util.CurrencyUtils;
import com.wiseasy.smartpos.cashierdemo.util.DeviceUtils;
import com.wiseasy.smartpos.cashierdemo.util.NfcLogoLocationUtils;
import com.wiseasy.smartpos.cashierdemo.util.SAXParserUtils;
import com.wiseasy.smartpos.cashierdemo.util.TLV;
import com.wiseasy.smartpos.cashierdemo.util.TLVList;
import com.wisepos.smartpos.InitPosSdkListener;
import com.wisepos.smartpos.WisePosException;
import com.wisepos.smartpos.WisePosSdk;
import com.wisepos.smartpos.cardreader.CardReader;
import com.wisepos.smartpos.cardreader.EmvCardListener;
import com.wisepos.smartpos.cardreader.MagCardResponse;
import com.wisepos.smartpos.cardreader.RfCardResponse;
import com.wisepos.smartpos.device.BeepFreqFlag;
import com.wisepos.smartpos.device.Device;
import com.wisepos.smartpos.keymanager.CipherKeyInfo;
import com.wisepos.smartpos.keymanager.KcvType;
import com.wisepos.smartpos.keymanager.KeyAlgType;
import com.wisepos.smartpos.keymanager.KeyManager;
import com.wisepos.smartpos.keymanager.KeyType;
import com.wisepos.smartpos.keymanager.PlainKeyInfo;
import com.wisepos.smartpos.pinpad.Pinpad;
import com.wisepos.smartpos.transaction.CvmType;
import com.wisepos.smartpos.transaction.InteractionPoint;
import com.wisepos.smartpos.transaction.ParametersType;
import com.wisepos.smartpos.transaction.PinBlockFormat;
import com.wisepos.smartpos.transaction.TransOption;
import com.wisepos.smartpos.transaction.TransProcess;
import com.wisepos.smartpos.transaction.TransProcessListener;
import com.wisepos.smartpos.transaction.TransReturnType;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {
    private Button initBtn;
    private Button startBtn;
    private Button stopBtn;
    private Button setidBtn;
    private Button clearLog;
    private Button showmore;
    private TextView mListTitle;
    private Spinner spTransType;
    private EditText etAmount,etAmountE1Lite;
    private RadioGroup rgSeephoneTransAgain, rgForceOnline;
    private RelativeLayout rlNFC;
    private RelativeLayout mivNfcLogo;
    private int transType = 0;
    public static WisePosSdk wisePosSdk = null;
    //The app can modify the transaction parameter configuration file and place it in this directory.
    //If there is no XML file in this directory, the configuration file in the resource file will be used.
    private static final String CONFIG_PATH = "/sdcard/WisePosCashierDemo/";
    private static final String CONFIG_FILE = "WisePosSdk_Configs.xml";
    private static KeyManager keyManager;
    private static TransProcess transProcess;
    private static CardReader cardReader;
    private static Pinpad pinpad;
    private static Device device;
    public static final String TAG = "TransProcessActivity";

    private byte[] track1;
    private byte[] track2;
    private byte[] track3;
    ListView lvShow;
    ScrollView scrollView;
    LogListViewAdapter logListViewAdapter;
    ArrayList<LogBean> logBeanArrayList = new ArrayList<LogBean>();
    // =============================================== Init ================================================= //
    /**
     * Transaction amount
     */
    private long amount;
    private long otherAmount = 0;
    /**
     * transaction no.
     */
    private int transNo;
    protected boolean isInitEmv = false;
    boolean emvStatus;
    private IPinpadDialog mPinpadDialog;
    private int pinType;
    private int cardType;
    private int showButtonFlag = 0;
    private String amountWithSymbol = "";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if(android.os.Build.MODEL.equals("P053")){
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }

        setContentView(R.layout.activity_main);

        wisePosSdk = WisePosSdk.getInstance();  //Obtain the WisePsoSdk object.
        Log.d("cashierdemo", "initPosSdk: process！");
        wisePosSdk.initPosSdk(this, new InitPosSdkListener() {  //Initialize the SDK and bind the service
            @Override
            public void onInitPosSuccess() {
                Log.d("cashierdemo", "initPosSdk: success!");
                keyManager = WisePosSdk.getInstance().getKeyManager();
                transProcess = WisePosSdk.getInstance().getTransProcess();
                cardReader = WisePosSdk.getInstance().getCardReader();
                pinpad = WisePosSdk.getInstance().getPinpad();
                device = WisePosSdk.getInstance().getDevice();

                Map<String, String> map = new HashMap<>();
                try {
                    map = device.getVersionInfo();
                } catch (WisePosException e) {
                    e.printStackTrace();
                }
                mListTitle.setText("Demo version: " + BuildConfig.VERSION_NAME + "\n"+"SDK version: " + map.get("sdk_p") + "\n");
            }

            @Override
            public void onInitPosFail(int i) {
                Log.d("cashierdemo", "initPosSdk: fail!");
            }
        });
        initView();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (MODEL.equals("T2")) {
            float floatX = 105;
            float floatY = 50;
            Log.d(TAG,"----- floatX = "+ floatX +" floatY ="+ floatY);

            NfcLogoLocationUtils.moveToWord(rlNFC, floatX,
                    floatY);
        }

        if (MODEL.equals("P5L")) {
            float floatX = 262;
            float floatY = 100;
            Log.d(TAG,"----- floatX = "+ floatX +" floatY ="+ floatY);

            NfcLogoLocationUtils.moveToWord(mivNfcLogo, floatX,
                    floatY);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (emvStatus) {
            try {
                cardReader.cancelOperation();//The force cancel operation can cancel the input of PIN and card searching action.
                transProcess.endTransaction();//end transaction.
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //Release resource
        if (emvStatus) {
            emvStatus = false;
            cardReader.cancelOperation();
            try {
                transProcess.endTransaction();
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }
        if (mPinpadDialog != null) {
            mPinpadDialog.dismiss();
        }
    }

    private void initView() {
        mListTitle = (TextView) findViewById(R.id.list_title);
        mListTitle.setText("Demo version: " + BuildConfig.VERSION_NAME + "\n");
        initBtn = findViewById(R.id.initBtn);
        startBtn = findViewById(R.id.startBtn);
        stopBtn = findViewById(R.id.stopBtn);
        spTransType = findViewById(R.id.sp_trans);
        etAmount = findViewById(R.id.etAmount);
        etAmountE1Lite = findViewById(R.id.etAmountE1Lite);
        setidBtn = findViewById(R.id.setid);
        initBtn.setOnClickListener(V -> loadKeyAndParameter());
        startBtn.setOnClickListener(v -> startTransaction());
        stopBtn.setOnClickListener(v -> cancelTransaction());
        setidBtn.setOnClickListener(v -> setIdentifier());
        lvShow = findViewById(R.id.lv_show);
        scrollView = findViewById(R.id.scrollView);
        clearLog = findViewById(R.id.clearLog);
        showmore = findViewById(R.id.showmore);
        logListViewAdapter = new LogListViewAdapter(logBeanArrayList, this);
        lvShow.setAdapter(logListViewAdapter);
        rgSeephoneTransAgain = findViewById(R.id.rg_seephone);
        rgForceOnline = findViewById(R.id.rg_forceonline);
        rlNFC = findViewById(R.id.rl_nfc);
        mivNfcLogo = findViewById(R.id.P5L_rl_nfc);

        rlNFC.setVisibility(View.GONE);
        mivNfcLogo.setVisibility(View.GONE);
        rgSeephoneTransAgain.setVisibility(View.GONE);
        rgForceOnline.setVisibility(View.GONE);
        findViewById(R.id.layout_1).setVisibility(View.GONE);
        findViewById(R.id.layout_2).setVisibility(View.GONE);
        showButtonFlag = 0;
        spTransType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (position == 0) {
                    transType = 0;
                } else if (position == 1) {
                    transType = 1;
                } else if (position == 2) {
                    transType = 9;
                } else if (position == 3) {
                    transType = 20;
                } else {
                    transType = -1;
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });

        clearLog.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                logBeanArrayList.clear();
                logListViewAdapter.notifyDataSetChanged();
            }
        });

        showmore.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(showButtonFlag == 0){
                    showButtonFlag = 1;
                    rgSeephoneTransAgain.setVisibility(View.VISIBLE);
                    rgForceOnline.setVisibility(View.VISIBLE);
                    findViewById(R.id.layout_1).setVisibility(View.VISIBLE);
                    findViewById(R.id.layout_2).setVisibility(View.VISIBLE);
                }else {
                    showButtonFlag = 0;
                    rgSeephoneTransAgain.setVisibility(View.GONE);
                    rgForceOnline.setVisibility(View.GONE);
                    findViewById(R.id.layout_1).setVisibility(View.GONE);
                    findViewById(R.id.layout_2).setVisibility(View.GONE);
                }
            }
        });
        initScrollHandler();
        if (DeviceUtils.isE1Lite()){
            findViewById(R.id.etAmountE1LiteLayout) .setVisibility(View.VISIBLE);
            etAmount.setVisibility(View.GONE);
            etAmountE1Lite.addTextChangedListener( new DecimalInputTextWatcher(etAmountE1Lite, 13, 2));

        }else {
            findViewById(R.id.etAmountE1LiteLayout).setVisibility(View.GONE);
        }
    }

    private float previousY,totalScrollY;
    private long startTime;
    @SuppressLint("ClickableViewAccessibility")
    private void initScrollHandler(){
        lvShow.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        v.getParent().requestDisallowInterceptTouchEvent(true);
                        startTime = System.currentTimeMillis();
                        previousY = event.getRawY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        v.getParent().requestDisallowInterceptTouchEvent(true);
                        float dy = previousY - event.getRawY();
                        previousY = event.getRawY();
                        totalScrollY = totalScrollY + dy;
                        if((dy >= 0 && lvShow.canScrollVertically(1) || ((dy <= 0) && lvShow.canScrollVertically(-1))) && !scrollView.canScrollVertically(1) ){
                            v.getParent().requestDisallowInterceptTouchEvent(true);
                        }else{
                            v.getParent().requestDisallowInterceptTouchEvent(false);
                        }
                        break;
                }
                return false;
            }
        });

    }

    // Set the identifier for transaction parameters, which is used to distinguish transaction parameters set by different apps to prevent confusion in parameter usage.
    // This interface only needs to be set once when the application starts up or when switching to other parameters.
    private void setIdentifier() {
        int ret = transProcess.setParamSetIdentifier("test1");
        if (ret == 0) {
            showTransactionReslut("setParamSetIdentifier success", 4);
        } else {
            showTransactionReslut("setParamSetIdentifier failed = " + ret, 7);
        }

    }

    //Used for installing test keys and transaction-related parameters for the first time.
    private void loadKeyAndParameter() {
        try {
            //Set the key identifier for the application, which is used to distinguish the key groups set by different applications and avoid confusion in key usage.
            // This interface needs to be set once when the application starts up or when switching between key groups is required.
            keyManager.setKeySetIdentifier("test1");
        } catch (WisePosException e) {
            e.printStackTrace();
        }

        if (injectMasterKey() == false) ;
        if (injectWorkKeyList() == false) ;
        if (loadEmvParameters() == false) return;
        isInitEmv = true;
    }

    private void startTransaction() {
        //Set transaction interaction points, set two interaction points for transaction completion selection and record reading.
        byte[] interactionPoints = new byte[2];
        interactionPoints[0] = InteractionPoint.IP_AFTER_FINAL_APP_SELECTION;
        interactionPoints[1] = InteractionPoint.IP_AFTER_READING_RECORD;

        try {
            transProcess.setInteractionPoints(interactionPoints, interactionPoints.length);
        } catch (WisePosException e) {
            e.printStackTrace();
        }
        //start transaction.
        doTransaction();
    }

    private void cancelTransaction() {
        try {
            cardReader.cancelOperation();
            transProcess.endTransaction();
        } catch (WisePosException e) {
            e.printStackTrace();
        }
    }
    //Import example of key in MK/SK key system.Import example of key in DUKPT key system.
    public Boolean injectMasterKey() {
        int result = -1;
        PlainKeyInfo key = new PlainKeyInfo();
        key.keyType = KeyType.KEY_TYPE_MK;
        key.keyAlg = KeyAlgType.KEY_ALG_TDEA_3K;
        key.keyIndex = 1;
        key.keyValue = "111111111111111111111111111111111111111111111111";
        key.kcvType = KcvType.KCV_TYPE_NORMAL;
        result = keyManager.injectPlainKey(key);
        if (result != ERR_SUCCESS) {
            showTransactionReslut("injectMKey failed", 7);
        }
        key.keyType = KeyType.KEY_TYPE_IPEK;
        key.keyAlg = KeyAlgType.KEY_ALG_TDEA_2K;
        key.keyIndex = 2;
        key.keyValue = "22222222222222222222222222222222";
        key.kcvType = KcvType.KCV_TYPE_NORMAL;
        key.iksn = Convection.hexString2Bytes("FFFF9876543210E00001");
        result = keyManager.injectPlainKey(key);
        if (result != ERR_SUCCESS) {
            showTransactionReslut("injectIPEK failed", 7);
        }
        showTransactionReslut("injectMasterKey success", 4);
        return true;
    }

    public Boolean injectWorkKeyList() {
        int result = 0;

        //jnjectMK/SK
        for (int i = 0; i < 3; i++) {
            CipherKeyInfo key = new CipherKeyInfo();
            int keyType = 0;

            switch (i) {
                case 0:
                    keyType = KEY_TYPE_PEK;
                    key.cipherKeyValue = "74A2824BF50CC44E74A2824BF50CC44E";
                    key.kcv = "ADC67D";
                    key.keyAlg = KeyAlgType.KEY_ALG_TDEA_2K;
                    break;
                case 1:
                    keyType = KeyType.KEY_TYPE_DEK;
                    key.cipherKeyValue = "7955A92F09E34C367955A92F09E34C367955A92F09E34C36";
                    key.kcv = "40826A";
                    key.keyAlg = KeyAlgType.KEY_ALG_TDEA_3K;
                    break;
                case 2:
                    keyType = KeyType.KEY_TYPE_MEK;
                    key.cipherKeyValue = "12AE588303EE33D512AE588303EE33D5";
                    key.kcv = "D2DB51";
                    key.keyAlg = KeyAlgType.KEY_ALG_TDEA_2K;
                    break;
            }

            key.keyType = keyType;
            key.keyIndex = 1;
            key.kcvType = KcvType.KCV_TYPE_NORMAL;
            key.typeProtectionKey = KeyType.KEY_TYPE_MK;
            key.indexProtectionKey = 1;
            result = keyManager.injectCipherKey(key);
            if (result != ERR_SUCCESS) {
                showTransactionReslut("injectWorkKey failed", 7);
            }
        }
        showTransactionReslut("injectWorkKey success", 4);
        return true;
    }

    //The program parses data from the transaction parameter configuration file and downloads the parameters to be used in the SDK.
    // Before calling the method to set the parameters, make sure that the identifier has been set.
    public boolean loadEmvParameters() {
        int result = -1;
        try {
            InputStream is = null;
            File file = new File(CONFIG_PATH, CONFIG_FILE);
            if (file.exists()) {
                FileInputStream fis = new FileInputStream(file);
                is = new BufferedInputStream(fis);
            } else {
                CLog.log(TAG, "The file xml does not exist!");
                is = getAssets().open(CONFIG_FILE);
            }
            if (is == null) {
                initFinished("Failed to read XML file!");
                return false;
            }
            CLog.log(TAG, "term default");
            showTransactionReslut("importTermDefault", 3);
            List<String> list_term_default = SAXParserUtils.getDefaultParameters(is);
            CLog.log(TAG, "importTermDefault|The number of Term Default is " + list_term_default.size());
            showTransactionReslut("importTermDefault|The number of Term Default is " + list_term_default.size(), 2);
            if (list_term_default.size() > 0) {
                transProcess.deleteAllParameters(ParametersType.PARAM_TYPE_TERM_DEFAULT);
                result = transProcess.addParameters(ParametersType.PARAM_TYPE_TERM_DEFAULT, list_term_default);
                CLog.log(TAG, "importTermDefault|result=" + result + "(" + result + ")");
                if (result != 0) {
                    initFinished("Term Default input fail: " + result);
                    return false;
                }
            }

            CLog.log(TAG, "addAID");
            showTransactionReslut("addAID", 3);
            List<String> list_aid = SAXParserUtils.getAidList(is);
            CLog.log(TAG, "addAID|The number of AIDs is " + list_aid.size());

            if (list_aid.size() > 0) {

                CLog.log(TAG, "delAllAID");
                transProcess.deleteAllParameters(ParametersType.PARAM_TYPE_AID);
                result = transProcess.addParameters(ParametersType.PARAM_TYPE_AID, list_aid);
                CLog.log(TAG, "addAID|result=" + result + "(" + result + ")");
                showTransactionReslut("addAID|result=" + result, 2);
                if (result != 0) {
                    initFinished("aid input fail" + result);
                    return false;
                }
            }

            CLog.log(TAG, "addCAPK");
            showTransactionReslut("addCAPK", 3);

            List<String> list_capk = SAXParserUtils.getCapkList(is);
            CLog.log(TAG, "addCAPK|The number of CAPKs is " + list_capk.size());
            showTransactionReslut("addCAPK|The number of CAPKs is " + list_capk.size(), 2);

            if (list_capk.size() > 0) {

                CLog.log(TAG, "delAllCAPK");
                transProcess.deleteAllParameters(ParametersType.PARAM_TYPE_CAPK);
                result = transProcess.addParameters(ParametersType.PARAM_TYPE_CAPK, list_capk);
                CLog.log(TAG, "addCAPK|result=" + result + "(" + result + ")");
                if (result != 0) {
                    initFinished("capk input fail" + result);
                    return false;
                }
            }

            CLog.log(TAG, "addRevocationCAPK");
            showTransactionReslut("addRevocationCAPK", 3);
            List<String> list_recapk = SAXParserUtils.getRevocationCapkList(is);
            CLog.log(TAG, "addRevocationCAPK|The number of Revocation CAPKs is " + list_recapk.size());
            showTransactionReslut("addRevocationCAPK|The number of Revocation CAPKs is " + list_recapk.size(), 2);
            if (list_recapk.size() > 0) {

                CLog.log(TAG, "delAllRevocationCAPK");
                transProcess.deleteAllParameters(ParametersType.PARAM_TYPE_CRL);
                result = transProcess.addParameters(ParametersType.PARAM_TYPE_CRL, list_recapk);
                CLog.log(TAG, "addRevocationCAPK|result=" + result + "(" + result + ")");
                if (result != 0) {
                    initFinished("Revocation capk input fail" + result);
                    return false;
                }
            }

            CLog.log(TAG, "importBlacklistPan");
            transProcess.deleteAllParameters(ParametersType.PARAM_TYPE_EXCEPTION_LIST);
            showTransactionReslut("importBlacklistPan", 3);
            List<String> list_pan_black = SAXParserUtils.getBlacklistPan(is);
            CLog.log(TAG, "importBlacklistPan|The number of BlacklistPan is " + list_pan_black.size());
            showTransactionReslut("importBlacklistPan|The number of BlacklistPan is " + list_pan_black.size(), 2);
            if (list_pan_black.size() > 0) {

                CLog.log(TAG, "deleteAllBlacklistPan");
                result = transProcess.addParameters(ParametersType.PARAM_TYPE_EXCEPTION_LIST, list_pan_black);
                CLog.log(TAG, "importBlacklistPan|result=" + result + "(" + result + ")");
                if (result != 0) {
                    initFinished("BlacklistPan input fail" + result);
                    return false;
                }
            }

            CLog.log(TAG, "importPid");
            showTransactionReslut("importPid", 3);
            transProcess.deleteAllParameters(ParametersType.PARAM_TYPE_VISA_DRL);
            List<String> list_pid = SAXParserUtils.getPidList(is);
            CLog.log(TAG, "importPid|The number of Pid is " + list_pid.size());
            showTransactionReslut("importPid|The number of Pid is " + list_pid.size(), 2);
            if (list_pid.size() > 0) {
                CLog.log(TAG, "deleteAllPid");
                result = transProcess.addParameters(ParametersType.PARAM_TYPE_VISA_DRL, list_pid);
                CLog.log(TAG, "importPid|result=" + result + "(" + result + ")");
                if (result != 0) {
                    initFinished("Pid input fail" + result);
                    return false;
                }
            }

            CLog.log(TAG, "importDrl");
            showTransactionReslut("importDrl", 3);
            transProcess.deleteAllParameters(ParametersType.PARAM_TYPE_AMEX_DRL);
            List<String> list_drl = SAXParserUtils.getDrlList(is);
            CLog.log(TAG, "importDrl|The number of Drl is " + list_drl.size());
            showTransactionReslut("importDrl|The number of Drl is " + list_drl.size(), 2);
            if (list_drl.size() > 0) {

                CLog.log(TAG, "deleteAllDrl");
                result = transProcess.addParameters(ParametersType.PARAM_TYPE_AMEX_DRL, list_drl);
                CLog.log(TAG, "importDrl|result=" + result + "(" + result + ")");
                if (result != 0) {
                    initFinished("Drl input fail" + result);
                    return false;
                }
            }

            CLog.log(TAG, "Init Finish");
            initFinished(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    private void doTransaction() {
        boolean seePhoneStartTransAgain = false;
        boolean forceOnline = false;
        emvStatus = true;
        cardType = 0;
        CLog.log(TAG, "PurchaseThread|run");

        if (DeviceUtils.isE1Lite()){
            String amt = etAmountE1Lite.getText().toString();
            if(amt.equals("")){
                showTransactionReslut("Please enter the transaction amount", 7);
                return;
            }
            double amountDou = Double.valueOf(amt) * 100;
            amount = (long) amountDou;
        } else {
            String amt = etAmount.getText().toString();
            if(amt.equals("")){
                showTransactionReslut("Please enter the transaction amount", 7);
                return;
            }
            amount = Long.parseLong(amt);
        }



        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdfData = new SimpleDateFormat("yyMMdd");
        SimpleDateFormat sdfTime = new SimpleDateFormat("HHmmss");
        showTransactionReslut("start transaction...", 3);
        transNo++;
        String currentCodeUSA = "5F2A020840";
        //Before starting a transaction, you need to set transaction parameters, mainly including transaction amount, transaction time, transaction date, transaction counter, etc.
        String transInitDataTlvList = String.format("9C01%02d", transType) + String.format("9F0206%012d", amount) +
                "9A03" + sdfData.format(cal.getTime()) + "9F2103" + sdfTime.format(cal.getTime()) +
                String.format("9F0306%012d", otherAmount) + String.format("9F4104%08d", transNo) + currentCodeUSA;

        showTransactionReslut("Transaction parameters:", 3);
        showTransactionReslut(transInitDataTlvList, 2);
        //When the transaction returns 'seephone', a new transaction needs to be initiated. Before initiating the transaction, please set the following configuration option to 'Yes'."
        if (rgSeephoneTransAgain.getCheckedRadioButtonId() == R.id.rb_seephone_yes){
            seePhoneStartTransAgain = true;
        }else{
            seePhoneStartTransAgain = false;
        }
        //When a transaction requires a forced online connection, please set the following configuration to 'Yes'; otherwise, set it to 'No'."
        if (rgForceOnline.getCheckedRadioButtonId() == R.id.rb_force_online_yes){
            forceOnline = true;
        }else{
            forceOnline = false;
        }
        //Set transaction configuration options as follows: transaction timeout of 30 seconds, no forced online support, and no skipping of PIN verification.
        TransOption transOption = new TransOption.TransOptionBuilder()
                .setMaxWaitingMs(300*1000)//120s
                .setForceOnline(forceOnline)
                .setSkipPinVerification(false)
                .setSupportSeePhoneStartTransAgain(seePhoneStartTransAgain)
                .build();

        showTransactionReslut("setMaxWaitingMs 30000", 2);
        device.controlLed(false,false,false,true);
        startTransaction(transOption, transInitDataTlvList);
        if (DeviceUtils.isE1Lite()) {
            TLVList tlvList = TLVList.fromBinary(transInitDataTlvList);
            TLV tag5F2A = tlvList.getTLV("5F2A");
            if (tag5F2A != null) {
                String currency = tag5F2A.getValue();
                if (!TextUtils.isEmpty(currency)) {
                    String etAmountMessage = CurrencyUtils.formatAmountByMinUnitWithStyleAndSymbol(String.valueOf(amount), Integer.parseInt(currency));
                    int result = WisePosSdk.getInstance().getGuestScreen().displayText(etAmountMessage);
                    if (result == ERR_SVR_INVALID_PARAMETER){
                        Toast.makeText(getApplicationContext(),"The amount exceeds the display length of the guest screen, which only supports displaying 10 characters.",Toast.LENGTH_LONG).show();
                    }

                    amountWithSymbol = etAmountMessage;
                    etAmount.setText(amountWithSymbol);
                }
            }

        }

    }
    //To start a transaction processing:
    protected void startTransaction(TransOption transRequest, String transDataTlvList) {
        new Thread(() -> {
            try {
                transProcess.startTransaction(transRequest, transDataTlvList, transProcessListener);
            } catch (WisePosException e) {
                showTransactionReslut("startTransaction error："+e, 2);
                //emvStatus = false;
                e.printStackTrace();
            }
        }).start();
    }
    //Transaction callback method concrete implementation example code
    private TransProcessListener transProcessListener = new TransProcessListener() {
        @Override
        public void onTransReturn(int returnType, int error, int interactionPoint) {
            emvStatus = false;
            String cvm = "";
            String outCome = "";
            TLVList tagList;
            TLV tag9F34 = null;
            TLV tagDF8129 = null;
            byte[] data = new byte[3];
            if (error == ERR_SUCCESS) {
                //
                //The transaction requires online processing.
                if (returnType == TransReturnType.RETURN_ONLINE_REQUEST) {
                    showTransactionReslut("Transaction need go online: ", 3);
                    showTransactionReslut(transProcess.getTlv("9F029F03959F1A9F1E9F249F269F279F339F349F359F6D9F6E505A9A9C5F205F245F255F285F2A5F2D5F34"), 2);
                    //The app needs to use the getTlv method to retrieve transaction-related data from the kernel, and then connect and send it to the backend for processing.
                    // It also needs to receive the data returned by the backend.
                    StringBuilder transData = new StringBuilder();
                    //After receiving the data from the backend, the app needs to set the online transaction result by using the tagDF818701 and passing the data received from the backend to the kernel.
                    transData.append("DF8187010100"). //set online Result
                            append("8A0230308906112233445566");
                    //To continue with the processing of the transaction.
                    try {
                        transProcess.continueTransaction(transData.toString(), transProcessListener);
                    } catch (WisePosException e) {
                        e.printStackTrace();
                    }
                } else if (returnType == TransReturnType.RETURN_INTERACTION_POINT) {
                    //The returned type is an interaction point.
                    showTransactionReslut("Transaction InteractionPoint "+interactionPoint, 3);
                    if (interactionPoint == InteractionPoint.IP_AFTER_FINAL_APP_SELECTION) {
                        try {
                            transProcess.setTlv("9F0206000000000100");
                            transProcess.setTlv("5F2A020840");
                            transProcess.continueTransaction(null, transProcessListener);
                        } catch (WisePosException e) {
                            e.printStackTrace();
                        }
                    }else if (interactionPoint == InteractionPoint.IP_AFTER_READING_RECORD) {
                        try {
                            String pan = transProcess.getTlv("5A");
                            if( pan.equals("") == false){
                                showTransactionReslut("pan:"+pan.substring(4), 3);
                            }
                            transProcess.continueTransaction(null, transProcessListener);
                        } catch (WisePosException e) {
                            e.printStackTrace();
                        }
                    } else {
                        try {
                            transProcess.continueTransaction(null, transProcessListener);
                        } catch (WisePosException e) {
                            e.printStackTrace();
                        }
                    }
                } else {

                    if(returnType == TransReturnType.RETURN_MSR_SUCCESS){
                        //In the transaction of magnetic stripe card, the SDK does not do any processing,
                        //directly returns the magnetic stripe success, and the magnetic stripe card track data is obtained by the application layer for post-processing.

                        //When the service code in the Track 2 data is '2' or '6', it is necessary to use an IC card for the transaction.
                        showTransactionReslut("Input online PIN...", 3);
                        magInputPin(CvmType.CVM_TYPE_ONLINE_PIN, 1, transType, amount);
                        device.controlLed(false,false,false,false);
                        return;
                    }
                    displayText();
                    device.controlLed(false,true,true,true);
                    //The returned type is the final transaction result.
                    if (returnType == TransReturnType.RETURN_OFFLINE_DECLINED) {
                        device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                        device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                        showTransactionReslut("Transaction Result: Offline Decline", 7);
                    }
                    if (returnType == TransReturnType.RETURN_ONLINE_DECLINED) {
                        device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                        device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                        showTransactionReslut("Transaction Result: Online Decline", 7);
                    }
                    if (returnType == TransReturnType.RETURN_OFFLINE_APPROVED) {
                        showTransactionReslut("Transaction Result: Offline Approve", 4);
                        if(cardType == CARD_SLOT_IC){
                            cvm = transProcess.getTlv("9f34");
                            if( cvm.equals("") == false){
                                tagList = TLVList.fromBinary(cvm);
                                tag9F34 = tagList.getTLV("9F34");
                                data = tag9F34.getBytesValue();
                                if((data[0]&0x0F) != 0x0F ){
                                    if((data[0]&0x03) == 0x03 || (data[0]&0x05) == 0x05 || (data[0]&0x1E) == 0x1E){
                                        showTransactionReslut("Cardholder signature on paper", 4);
                                    }
                                }
                            }
                        }
                        if(cardType == CARD_SLOT_RF){
                            outCome = transProcess.getTlv("DF8129");
                            if(outCome.equals("") == false){
                                tagList = TLVList.fromBinary(outCome);
                                tagDF8129 = tagList.getTLV("DF8129");
                                data = tagDF8129.getBytesValue();
                                if((data[3]&0x10) == 0x10){
                                    showTransactionReslut("Cardholder signature on paper", 4);
                                }
                            }
                        }
                    }
                    if (returnType == TransReturnType.RETURN_ONLINE_APPROVED) {
                        showTransactionReslut("Transaction Result: Online Approve", 4);
                        if(cardType == CARD_SLOT_IC) {
                            cvm = transProcess.getTlv("9f34");
                            if (cvm.equals("") == false) {
                                tagList = TLVList.fromBinary(cvm);
                                tag9F34 = tagList.getTLV("9F34");
                                data = tag9F34.getBytesValue();
                                if((data[0]&0x0F) != 0x0F ) {
                                    if ((data[0] & 0x03) == 0x03 || (data[0] & 0x05) == 0x05 || (data[0] & 0x1E) == 0x1E) {
                                        showTransactionReslut("Cardholder signature on paper", 4);
                                    }
                                }
                            }
                        }
                        if(cardType == CARD_SLOT_RF){
                            outCome = transProcess.getTlv("DF8129");
                            if(outCome.equals("") == false){
                                tagList = TLVList.fromBinary(outCome);
                                tagDF8129 = tagList.getTLV("DF8129");
                                data = tagDF8129.getBytesValue();
                                if((data[3]&0x10) == 0x10){
                                    showTransactionReslut("Cardholder signature on paper", 4);
                                }
                            }
                        }
                    }
                    if(returnType == TransReturnType.RETURN_GAC2_DECLINED){
                        device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                        device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                        showTransactionReslut("Transaction Result: Online Decline", 7);
                    }
                    showTransactionReslut(transProcess.getTlv("9F029F039F079F099F109F119F129F1A9F1E9F249F269F279F339F349F359F369F369F539F6D9F6E9F7C505A959A9C82845F205F245F255F285F2A5F2D5F305F34"), 2);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    device.controlLed(false,false,false,false);
                }
            } else {
                displayText();
                //In certain Wiseasy SmartPOS models, the magnetic stripe card reader and contactless card reader are placed close together for aesthetic reasons (currently, this issue is present in the P5SE model).
                // For composite cards supporting both interfaces, when a cardholder swipes a magnetic stripe card, the terminal may first detect the contactless card. However, due to the card being in motion at that moment, the reading of the contactless card may ultimately fail.
                // If the transaction fails, the callback function onTransReturn will be invoked.
                //void onTransReturn(int returnType, int error, int interactionPoint);
                //In the above scenario, the error returned will be ERR_MSR_SWIPE_WHEN_RF, indicating a conflict between the magnetic and contactless interfaces during the magnetic stripe card swiping. In this situation, the app can invoke the readMagCardAfterRfFail method to retrieve the magnetic stripe card data.
                if(error == ERR_MSR_SWIPE_WHEN_RF){
                    MagCardResponse  magCardResponse = new MagCardResponse();
                    int ret = cardReader.readMagCardAfterRfFail(magCardResponse);
                    if (ret == 0){
                        //In the transaction of magnetic stripe card, the SDK does not do any processing,
                        //directly returns the magnetic stripe success, and the magnetic stripe card track data is obtained by the application layer for post-processing.

                        //When the service code in the Track 2 data is '2' or '6', it is necessary to use an IC card for the transaction.
                        showTransactionReslut("Input online PIN...", 3);
                        magInputPin(CvmType.CVM_TYPE_ONLINE_PIN, 1, transType, amount);
                        device.controlLed(false,false,false,false);
                        return;
                    }
                }
                //If the transaction encounters an error and returns "Transaction Termination,"
                // app can analyze the returned error code to determine the cause of the transaction error.
                if (returnType == TransReturnType.RETURN_TRANS_TERMINATED) {
                    device.controlLed(true,true,false,true);
                    device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                    device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 100);
                    showTransactionReslut("Transaction Result: Terminated", 7);

                }
               // showTransactionReslut("Error Code: " + error + ",Click to get diagnostic information", 7);
                showTransactionReslut("Error Code: " + error, 7);
                if(error == ERR_USE_ANOTHER_INTERFACE){
                    showTransactionReslut("Please use another interface ", 7);
                }
                if(error == ERR_SEE_PHONE){
                    showTransactionReslut("Please see phone", 7);
                }
                if (error==ERR_IC_MAG_ANOTHER_CARD){
                    String dfa101 = transProcess.getTlv("DFA101");
                    if (!dfa101.equals("")) {
                        String dfa101Value = dfa101.substring(8);
                        showTransactionReslut("DFA101="+dfa101, 7);
                        if (dfa101Value.equals("0838")){//Hex
                            //Select APP时 应用已锁
                            showTransactionReslut("十进制 DFA101= 2104", 7);
                        }else if (dfa101Value.equals("089E")){
                            //GPO时 APDU返回非9000
                            showTransactionReslut("十进制 DFA101= 2206", 7);
                        }
                    }
                }
                //If an IC card transaction requires the processing of a fallback transaction flow, the following error code is used to determine if a fallback is required.
                //When fallback is required, the two error codes ERR_NO_SUPPORTED_APP,ERR_P_PSE_APDU_NOT_9000 and ERR_SEL_APP_APDU_NOT_9000 are determined and the application can restart the transaction and specify that it is searching for the magnetic stripe card.
                //If a fallback transaction does not need to be processed, this determination is ignored.
                if(cardType == CARD_SLOT_IC){
                    if(error == ERR_NO_SUPPORTED_APP || error == ERR_P_PSE_APDU_NOT_9000 || error == ERR_SEL_APP_APDU_NOT_9000){
                        showTransactionReslut("Need to perform a fallback transaction", 7);
                    }
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                device.controlLed(false,false,false,false);
                if (error != 0){
                    showTransactionReslut( "Click to get diagnostic information", 3);
                }
            }

        }

        @Override
        public void onWaitCard() {
            //During the transaction callback, including card-related methods, the app needs to invoke the openEmvCard method.
            try {
                if (MODEL.equals("T2")) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            rlNFC.setVisibility(View.VISIBLE);
                        }
                    });
                }
                if (MODEL.equals("P5L")) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mivNfcLogo.setVisibility(View.VISIBLE);
                        }
                    });
                }
                showTransactionReslut("waiting card...", 3);
                cardReader.openEmvCard(CARD_SLOT_IC | CARD_SLOT_RF | CARD_SLOT_MAG, 30000, emvCardListener);
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onMultiAppSelection(List<String> candidateAppDataTlvList, boolean isFirstSelect) {
            //When multiple application selection is required during the transaction, the app needs to implement the callback method.
            // The app should present the provided data to the cardholder for application selection and return the selected result to the kernel.
            showTransactionReslut("Multi App Selection...", 3);
            CLog.log(TAG, "onRequestAppSelect candidateAppDataTlvList: " + candidateAppDataTlvList + " isFirstSelect:" + isFirstSelect);
            onRequestAppSelectDialog(candidateAppDataTlvList, isFirstSelect);
        }

        @Override
        public void onInputPin(int i) {
            //The callback for PIN verification requires the app to provide PIN input. The type of PIN verification (online or offline) can be determined by the returned int value.
            if (i == CvmType.CVM_TYPE_ONLINE_PIN) {
                showTransactionReslut("Input online PIN...", 3);
                pinType = 1;
            } else {
                //For offline PIN verification, app can use the getTlv method to retrieve the remaining PIN verification attempts.
                String tryCount = transProcess.getTlv("9F17");
                pinType = 2;
                showTransactionReslut("Input offline PIN...", 3);
            }
            doInputPin(i, pinType, transType, amount);
        }

        @Override
        public void onPresentCardAgain() {
            //This callback method is only called when a second card issuance is required in contactless transactions. It is used to complete the re-card search, and the kernel continues to process issuer authentication data and script handling.
            try {
                cardReader.openEmvCard(CARD_SLOT_RF, 30000, emvCardListener);
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onNfcCardReadingCompleted(int i) {
            //Contactless transaction card reading completion callback method.
            // The main purpose of this method is for the application to determine that the card reading process has been completed.
            // The application can use this callback to trigger actions such as sounding the buzzer or illuminating an LED to prompt the user to remove the card.
            device.beep(BeepFreqFlag.BEEP_NORMAL_FREQ, 200);
            showTransactionReslut("Please remove the card. ", 3);
            try {
                transProcess.continueTransaction(null, transProcessListener);
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onObtainData(int i, byte[] bytes, byte[] bytes1) {
            try {
                // Assuming the default value for 'i' is 1 and the kernel provides data in tag format.
                // bytes[]: Tag values to be retrieved from the kernel {0xDF, 0x81, 0x83, 0x17}
                // bytes1[]: Additional data provided by the kernel to the application {0x5A, 0xXX, ...}
                // The application layer retrieves all transaction amounts for this card from the transaction records using the value of 5A, and performs an accumulation calculation to obtain the cumulative amount.
                // The cumulative amount, for example, 10.00, is then set to the kernel using the setTlv method, and continueTransaction method is called to proceed with the transaction processing.
//                transProcess.setTlv("DF81831706000000001000");
//                transProcess.continueTransaction(null, transProcessListener);
                //The application can also directly pass the TLV data to be set using the continueTransaction method.
                transProcess.continueTransaction("DF81831706000000001000", transProcessListener);
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }
    };
    //Callback interface for the openEmvCard method.
    private EmvCardListener emvCardListener = new EmvCardListener() {
        @Override
        public void onError(int errorCode) {
            //Card search failed. An integer error code is returned. The app needs to terminate the transaction processing.
            try {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        rlNFC.setVisibility(View.GONE);
                        mivNfcLogo.setVisibility(View.GONE);
                    }
                });
                showTransactionReslut("Open Emv Card Result: error = " + errorCode, 7);

                //If an IC card transaction requires the processing of a fallback transaction flow, the following error code is used to determine if a fallback is required.
                //When fallback is required, the two error codes ERR_ICC_NO_ATR and ERR_ICC_INVALID_ATR are determined and the application can restart the transaction and specify that it is searching for the magnetic stripe card.
                //If a fallback transaction does not need to be processed, this determination is ignored.
                if(errorCode == ERR_ICC_NO_ATR || errorCode == ERR_ICC_INVALID_ATR){
                    showTransactionReslut("Need to perform a fallback transaction", 7);
                }
                transProcess.endTransaction();
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onMagCardDetected(MagCardResponse magCardResponse) {
            //Read magnetic stripe card successful.
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    rlNFC.setVisibility(View.GONE);
                    mivNfcLogo.setVisibility(View.GONE);
                }
            });
            cardType = CARD_SLOT_MAG;
            showTransactionReslut("MAG card has been swiped", 3);
            track1 = magCardResponse.getTrack1Data();
            track2 = magCardResponse.getTrack2Data();
            track3 = magCardResponse.getTrack3Data();
            showMagCardData(magCardResponse, 2);
            showTransactionReslut("processing...", 3);
            try {
                transProcess.continueTransaction(null, transProcessListener);
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onIcDetected(String s) {
            //Read contact card successful.
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    rlNFC.setVisibility(View.GONE);
                    mivNfcLogo.setVisibility(View.GONE);
                }
            });
            cardType = CARD_SLOT_IC;
            showTransactionReslut("IC card has been inside", 3);
            showTransactionReslut("processing...", 3);
            device.controlLed(false,true,false,true);
            try {
                transProcess.continueTransaction(null, transProcessListener);
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onRfCardDetected(RfCardResponse rfCardResponse) {
            //Read contactless card successful.
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    rlNFC.setVisibility(View.GONE);
                    mivNfcLogo.setVisibility(View.GONE);
                }
            });
            cardType = CARD_SLOT_RF;
            showTransactionReslut("Contactless card has been tapped", 3);
            showTransactionReslut("processing...", 3);
            device.controlLed(false,true,false,true);
            try {
                transProcess.continueTransaction(null, transProcessListener);
            } catch (WisePosException e) {
                e.printStackTrace();
            }
        }
    };

    private void showMagCardData(MagCardResponse magData, int rgb){
        StringBuilder sbTips = new StringBuilder();

        track1 = magData.getTrack1Data();
        track2 = magData.getTrack2Data();
        track3 = magData.getTrack3Data();
        sbTips.append("MAG card Data:").append("\n").
                append("trackStandard: ").append(magData.trackStandard);
        if(magData.track1Status == 0){
            sbTips.append("track1Status: ").append(magData.track1Status).append("\n").
                    append("track1Data: ").append(new String(magData.track1Data)).append("\n");
        }
        if(magData.track2Status == 0){
            sbTips.append("track2Status: ").append(magData.track2Status).append("\n").
                    append("track2Data: ").append(new String(magData.track2Data)).append("\n");
        }
        if(magData.track3Status == 0){
            sbTips.append("track3Status: ").append(magData.track3Status).append("\n").
                    append("track3Data: ").append(new String(magData.track3Data)).append("\n");
        }
        showTransactionReslut(sbTips.toString(), rgb);
    }

    private void magInputPin(int cvmFlag, int pinType, int transType, Long amount){
        Bundle bundle = new Bundle();
        /**
         * Customize configuration display information(no examples)
         */
        bundle.putInt("PinType", pinType);
        bundle.putInt("Title", transType);
        bundle.putLong("Amount", amount);
        bundle.putString("Tips", "Press OK if no PIN");
        bundle.putString("PwdHint", "Enter Password");
        if (DeviceUtils.isE1Lite()){
            bundle.putString("amountWithSymbol",amountWithSymbol);
        }

        new Handler(Looper.getMainLooper())
                .post(() -> {
                    mPinpadDialog = new PinpadDialog(MainActivity.this, new Intent().putExtras(bundle), new PinpadDialog.PinpadResultListener() {
                        @Override
                        public void onPinpadResultListener(int code, int sw1sw2) {
                            handleMagPinpadResult(code, sw1sw2);
                        }
                    });
                    mPinpadDialog.show();
                });

    }


    private void doInputPin(int cvmFlag, int pinType, int transType, Long amount) {
        Bundle bundle = new Bundle();
        /**
         * Customize configuration display information(no examples)
         */
        bundle.putInt("PinType", pinType);
        bundle.putInt("Title", transType);
        bundle.putLong("Amount", amount);
        bundle.putString("Tips", "Press OK if no PIN");
        if (cvmFlag == CvmType.CVM_TYPE_ONLINE_PIN) {
            bundle.putString("PwdHint", "Enter Password");
        } else {
            bundle.putString("PwdHint", "Enter PIN");
        }

        if (DeviceUtils.isE1Lite()){
            bundle.putString("amountWithSymbol",amountWithSymbol);
        }

        new Handler(Looper.getMainLooper())
                .post(() -> {
                    mPinpadDialog = new PinpadDialog(MainActivity.this, new Intent().putExtras(bundle), new PinpadDialog.PinpadResultListener() {
                        @Override
                        public void onPinpadResultListener(int code, int sw1sw2) {
                            handlePinpadResult(code, sw1sw2);
                        }
                    });
                    mPinpadDialog.show();
                });
    }

    /**
     * handle the result of Pinpad
     *
     * @param code code
     */
    private void handlePinpadResult(int code, int sw1sw2) {
        try {
            if (code == ERR_SUCCESS && pinType == 1) {
                byte[] out = new byte[16];
                int[] outLen = new int[1];

                String pan = updatePan();
                showTransactionReslut("PinBlockFormat FORMAT_ISO9564_0", 3);
                //If it is online PIN verification, after entering the PIN, you need to call the getPinBlock method to obtain the online PIN block value.
                pinpad.getPinBlock(1, KeyType.KEY_TYPE_PEK, 0, PinBlockFormat.FORMAT_ISO9564_0, pan, null, 0, out, outLen);
                transProcess.continueTransaction(null, transProcessListener);
                CLog.log(TAG, "Card No = pan=: " + pan );
                String pinBlock = Convection.byte2HexStr(out);
                CLog.log(TAG, "pinBlock =: " + pinBlock );
            }else if(code == ERR_SVR_PINPAD_SCERRN_OFF){
                showTransactionReslut("start pin input error = " + code, 7);
                transProcess.endTransaction();
            }else{
                transProcess.continueTransaction(null, transProcessListener);
            }
        } catch (WisePosException e) {
            e.printStackTrace();
        }
    }

    boolean isOnlinePinError = false;
    private void handleMagPinpadResult(int code, int sw1sw2) {
        if (code == ERR_SUCCESS) {
            byte[] out = new byte[16];
            int[] outLen = new int[1];

            String strTrack2 = null;
            if(track2 != null){
                strTrack2 = new String(track2);
            }

            if (!TextUtils.isEmpty(strTrack2)) {
                String[] pan = strTrack2.split("=");
                showTransactionReslut("Card No.:"+pan[0], 2);
                showTransactionReslut("PinBlockFormat FORMAT_ISO9564_0", 3);
                //If it is online PIN verification, after entering the PIN, you need to call the getPinBlock method to obtain the online PIN block value.
                pinpad.getPinBlock(1, KeyType.KEY_TYPE_PEK, 0, PinBlockFormat.FORMAT_ISO9564_0, pan[0], null, 0, out, outLen);
                CLog.log(TAG, "Card No = pan=: " + pan[0] );
                String pinBlock = Convection.byte2HexStr(out);
                CLog.log(TAG, "pinBlock =: " + pinBlock );
            }
            showTransactionReslut("Transaction Result: Magnetic card is successfully ", 4);
        }else {
            if (ERR_PIN_BYPASS == code) {
                showTransactionReslut("Input Online Pin By Pass", 3);
                showTransactionReslut("Transaction Result: Magnetic card is successfully ", 4);
            }else {
                showTransactionReslut("Input Online Pin Error:"+code, 3);
                showTransactionReslut("Transaction Result: Terminated", 7);
            }

        }
        displayText();
    }

    public void showTransactionReslut(String mesage, int rgb) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                LogBean logBean = new LogBean();
                logBean.log = mesage;
                logBean.colorIndex = rgb;
                logBeanArrayList.add(logBean);
                logListViewAdapter.notifyDataSetChanged();
            }
        });
    }

    public void initFinished(final String msg) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (msg != null) {
                    LogBean logBean = new LogBean();
                    logBean.log = "Fail, " + msg;
                    logBean.colorIndex = 7;
                    logBeanArrayList.add(logBean);
                    logListViewAdapter.notifyDataSetChanged();
                } else {
                    LogBean logBean = new LogBean();
                    logBean.log = "Success";
                    logBean.colorIndex = 4;
                    logBeanArrayList.add(logBean);
                    logListViewAdapter.notifyDataSetChanged();
                }
            }
        });
    }

    private String updatePan() {
        String pan = "1234567887654321";
        String panTlv = "";
        panTlv = transProcess.getTlv("5A");
        if (panTlv.equals("") != true) {
            pan = panTlv.substring(4);
        } else {
            panTlv = transProcess.getTlv("57");
            if (panTlv != null) {
                pan = panTlv.substring(4, panTlv.indexOf("D"));
            } else {
                pan = "";
            }
        }
        return pan;
    }

    private int indexAppSelected = 0;

    private void onRequestAppSelectDialog(List<String> candidateAppDataTlvList, boolean isFirstSelect) {
        indexAppSelected = 0;
        final String[] radioItems = new String[candidateAppDataTlvList.size()];
        int i = 0;

        for (String candidateAppDataTlv : candidateAppDataTlvList) {
            CLog.log(TAG, "tlv:" + candidateAppDataTlv);
            radioItems[i++] = getAppLable(candidateAppDataTlv);
        }

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                AlertDialog.Builder radioDialog = new AlertDialog.Builder(MainActivity.this);
                radioDialog.setTitle("App Select:");
                radioDialog.setCancelable(false);
                radioDialog.setSingleChoiceItems(radioItems, 0, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        indexAppSelected = which;
                    }
                });
                radioDialog.setPositiveButton("confirm", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                byte[] valueIndex = new byte[1];
                                valueIndex[0] = (byte) indexAppSelected;
                                TLV tlv = TLV.fromData("DF818309", valueIndex);
                                try {
                                    transProcess.continueTransaction(Convection.byte2HexStr(tlv.getRawData()), transProcessListener);
                                } catch (WisePosException e) {
                                    e.printStackTrace();
                                }
                            }
                        }).start();
                    }
                });
                radioDialog.create().show();
            }
        });
    }

    private String getAppLable(String candidateAppDataTlv) {
        byte[] data = Convection.hexString2Bytes(candidateAppDataTlv);
        TLV tag61 = TLV.fromRawData(data, 0);
        TLVList tagList61 = TLVList.fromBinary(tag61.getBytesValue());
        TLV tag50 = null;
        TLV tag9F12 = null;
        TLV tag9F11 = null;
        TLV tag6F = null;
        TLV tag84 = null;
        String name = "";

        tag84 = tagList61.getTLV("84");
        if (tag84 != null){
            name = Convection.byte2HexStr(tag84.getBytesValue());
        }

        tag50 = tagList61.getTLV("50");
        if (tag50 != null){
            name = new String(tag50.getBytesValue());
        }

        tag6F = tagList61.getTLV("6F");
        if (tag6F != null){
            TLVList tagList6F = TLVList.fromBinary(tag6F.getBytesValue());
            TLV tagA5 = tagList6F.getTLV("A5");
            TLVList tagList = TLVList.fromBinary(tagA5.getBytesValue());

            tag9F11 = tagList.getTLV("9F11");
            if (tag9F11 != null && tag9F11.getByteValue() == 0x01){
                tag9F12 = tagList.getTLV("9F12");
                if (tag9F12 != null){
                    name = new String(tag9F12.getBytesValue());
                    return name;
                }

                tag9F12 = tagList61.getTLV("9F12");
                if (tag9F12 != null){
                    name = new String(tag9F12.getBytesValue());
                    return name;
                }
            }

            tag84 = tagList6F.getTLV("84");
            if (tag84 != null){
                name = Convection.byte2HexStr(tag84.getBytesValue());
            }

            tag50 = tagList.getTLV("50");
            if (tag50 != null){
                name = new String(tag50.getBytesValue());
            }

            return name;
        }

        return name;
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.i(TAG, "onConfigurationChanged: "+newConfig);
    }

    private void displayText(){
        if (DeviceUtils.isE1Lite()){
            WisePosSdk.getInstance().getGuestScreen().displayText("Welcome");
        }
    }




    static class DecimalInputTextWatcher implements TextWatcher {

        private static final String Period = ".";
        private static final String Zero = "0";

        private EditText editText = null;

        private int decimalDigits;
        private int totalDigits;


        public DecimalInputTextWatcher(EditText editText, int totalDigits, int decimalDigits) {
            if (editText == null) {
                throw new RuntimeException("editText can not be null");
            }
            this.editText = editText;
            if (totalDigits <= 0)
                throw new RuntimeException("totalDigits must > 0");
            if (decimalDigits <= 0)
                throw new RuntimeException("decimalDigits must > 0");
            this.totalDigits = totalDigits;
            this.decimalDigits = decimalDigits;
        }

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

        }

        @Override
        public void afterTextChanged(Editable editable) {
            try {
                String s = editable.toString();
                editText.removeTextChangedListener(this);

                if (s.contains(Period)) {
                    if (s.length() - 1 - s.indexOf(Period) > decimalDigits) {
                        s = s.substring(0, s.indexOf(Period) + decimalDigits + 1);
                        editable.replace(0, editable.length(), s.trim());
                    }
                    if (s.indexOf(Period) > 10){
                        s = s.substring(0, s.indexOf(Period));
                        editable.replace(0, editable.length(), s.trim());
                        editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(totalDigits-1)});
                    } else {
                        editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(totalDigits)});
                    }

                } else if (s.length() > 10){
                    if (s.charAt(10) != '.'){
                        s = s.substring(0, 10);
                        editable.replace(0, editable.length(), s.trim());
                    }
                }

                if (s.trim().equals(Period)) {
                    s = Zero + s;
                    editable.replace(0, editable.length(), s.trim());
                }

                if (s.startsWith(Zero) && s.trim().length() > 1) {
                    if (!s.substring(1, 2).equals(Period)) {
                        editable.replace(0, editable.length(), Zero);
                    }
                }
                editText.addTextChangedListener(this);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}